home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Pascal / Games / Showwit! 1.0 / source code / Source / CShGameDirector.p < prev    next >
Encoding:
Text File  |  1996-02-04  |  34.4 KB  |  1,293 lines  |  [TEXT/PJMM]

  1. {****************************************************}
  2. {}
  3. {        CShGameDirector.p                                                                                                                                                                                    }
  4. {}
  5. {        Director for the main, game playing window of the Showwit application.                     }
  6. {}
  7. {****************************************************}
  8.  
  9.  
  10. unit CShGameDirector;
  11.  
  12. interface
  13.  
  14.     uses
  15.         TCL, ShIntf;
  16.  
  17. implementation
  18.  
  19.     uses
  20.         Script;
  21.  
  22.     const
  23.  
  24.         { Dimensions. }
  25.  
  26.         kGameWindh = 504;
  27.         kGameWindv = 290;
  28.  
  29.         kEdgeMarginh = 20;
  30.         kEdgeMarginv = 10;
  31.  
  32.         kTableLenh = kGameWindh - 2 * kEdgeMarginh;
  33.         kTableLenv = 18 * 6 + 2;    { 6 rows, 18 pixels each, plus 2 pixels for the top and bottom. }
  34.         kTablePosh = kEdgeMarginh;
  35.         kTablePosv = 126;
  36.  
  37.         kStartLenh = 200;
  38.         kStartLenv = kIconPixels;
  39.         kStartPosh = (kGameWindh - kStartLenh) div 2;
  40.         kStartPosv = kGameWindv - kStartLenv - kEdgeMarginv;
  41.  
  42.         kTargetPosh = kEdgeMarginh;
  43.         kTargetPosv = kEdgeMarginv;
  44.  
  45.         kControlIconMargin = 24;
  46.  
  47.         kHelpPosh = kGameWindh - kIconPixels - kEdgeMarginh;
  48.         kHelpPosv = kGameWindv - kIconPixels - kEdgeMarginv;
  49.  
  50.         kControlsLenh = kIconPixels * 2 + kControlIconMargin;
  51.         kControlsLenv = kIconPixels;
  52.         kControlsPosh = kHelpPosh - kControlsLenh - kControlIconMargin;
  53.         kControlsPosv = kGameWindv - kControlsLenv - kEdgeMarginv;
  54.  
  55.         kRecordSpacing = 24;
  56.  
  57.         kNameLenh = 140;
  58.         kNameLenv = 16;
  59.         kNamePosh = 0;
  60.         kNamePosv = 0;
  61.  
  62.         kMovesLenh = 140;
  63.         kMovesLenv = 16;
  64.         kMovesPosh = 0;
  65.         kMovesPosv = kNameLenv + kRecordSpacing;
  66.  
  67.         kTimeLenv = 16;
  68.         kTimeSpacing = 10;
  69.  
  70.         kHoursLenh = 20;
  71.         kHoursLenv = kTimeLenv;
  72.         kHoursPosh = 0;
  73.         kHoursPosv = kMovesPosv + kMovesLenv + kRecordSpacing;
  74.  
  75.         kMinutesLenh = 20;
  76.         kMinutesLenv = kTimeLenv;
  77.         kMinutesPosh = kHoursLenh + kTimeSpacing;
  78.         kMinutesPosv = kHoursPosv;
  79.  
  80.         kSecondsLenh = 20;
  81.         kSecondsLenv = kTimeLenv;
  82.         kSecondsPosh = kMinutesPosh + kSecondsLenh + kTimeSpacing;
  83.         kSecondsPosv = kHoursPosv;
  84.  
  85.         kDataLenh = kNameLenh;
  86.         kDataLenv = kNameLenv + kRecordSpacing + kMovesLenv + kRecordSpacing + kTimeLenv;
  87.         kDataPosh = kGameWindh - kDataLenh - kEdgeMarginh;
  88.         kDataPosv = kEdgeMarginv + 80;
  89.  
  90.     type
  91.         BooleanPtr = ^Boolean;
  92.         BooleanHandle = ^BooleanPtr;
  93.  
  94.     const
  95.         kActivePlaySleepTime = 0;
  96.         kLoopTime = 1036800000;    { = 200 days in Ticks. Remember that maxLongInt = 2147483647. }
  97.                                                                                             { So we can have played up to 200 days before pausing, then 200 }
  98.                                                                                             { days afterwards, and can add the times without overflow. Silly, }
  99.                                                                                             { but the problems from overflow are avoided. }
  100.  
  101.     const
  102.         kSTRGameNoTargetPICTs = 1;
  103.         kSTRGameNoColorQDandNoBWTargetPICTs = 2;
  104.         kSTRGameStartIndex = 3;
  105.  
  106.  
  107. {****************************************************}
  108. {}
  109. {        IShGameDirector                                                                                                                                                                                            }
  110. {}
  111. {        Construction of the game director object.                                                                                                                 }
  112. {}
  113. {****************************************************}
  114.  
  115.     procedure CShGameDirector.IShGameDirector (aSupervisor: CShApp);
  116.  
  117.         var
  118.             theTileCount: TileRange;
  119.  
  120.         { Obtain and act upon the preferences for animation and sound. }
  121.         procedure InitAnimationAndSound;
  122.  
  123.             var
  124.                 theFlagHandle: BooleanHandle;
  125.  
  126.         begin { InitAnimationAndSound }
  127.             theFlagHandle := nil;
  128.  
  129.             itsShApp.PreferencesFile.GetPref(kPrefAnimationOn, Handle(theFlagHandle));
  130.             if theFlagHandle = nil then begin
  131.                 fAnimate := kAnimation;
  132.             end { if }
  133.             else begin
  134.                 fAnimate := theFlagHandle^^;
  135.             end; { else }
  136.  
  137.             itsShApp.PreferencesFile.GetPref(kPrefSoundOn, Handle(theFlagHandle));
  138.             if theFlagHandle = nil then begin
  139.                 fSound := kSound;
  140.             end { if }
  141.             else begin
  142.                 fSound := theFlagHandle^^;
  143.             end; { else }
  144.  
  145.             if fSound then begin
  146.                 SATSoundOn;
  147.             end { if }
  148.             else begin
  149.                 SATSoundOff;
  150.             end; { else }
  151.  
  152.             { Set the flag so that we know to play the welcoming sound. }
  153.             fWelcomingSoundPlayed := FALSE;
  154.         end; { InitAnimationAndSound }
  155.  
  156.         procedure GetPICTIDsForTargets;
  157.  
  158.             var
  159.                 theCounter, theLoadCount: Integer;
  160.                 theTargetBase: Integer;
  161.                 rHandle: Handle;
  162.                 rID: Integer;
  163.                 rType: ResType;
  164.                 rName: Str255;
  165.  
  166.                 fi: FailInfo;
  167.  
  168.             procedure HandleFailure (error: Integer;
  169.                                             message: LongInt);
  170.  
  171.             begin { HandleFailure }
  172.                 { Before exiting from the application, have to SetResLoad(TRUE), }
  173.                 { otherwise the Finder's code resource will not be loaded. }
  174.                 { See THINK Reference - SetResLoad for details. }
  175.  
  176.                 SetResLoad(TRUE);
  177.  
  178.                 rHandle := nil;
  179.  
  180.                 ErrorAlert(error, message);
  181.  
  182.                 { Debugging project - Compile exceptions with Debugging on }
  183.                 { otherwise the next call will crash. }
  184.  
  185.                 ExitApplication;
  186.             end; { HandleFailure }
  187.  
  188.         begin { GetPICTIDsForTargets }
  189.             { When this routine commences, the main event loop has not commenced, }
  190.             { so there is no main level failure handler. That's okay, because if }
  191.             {    something goes wrong here, we will want to just make things safe, }
  192.             {    put up an error alert and quit anyway. }
  193.  
  194.             { Set the safe conditions. }
  195.  
  196.             rHandle := nil;
  197.  
  198.             itsNumTargetPICTs := 0;
  199.             itsTargetPICTIDs := nil;
  200.  
  201.             { Select the kind of pictures to look for. }
  202.             if gSystem.hasColorQD then begin
  203.                 theTargetBase := PICTTargetColourBase;
  204.             end { if }
  205.             else begin
  206.                 theTargetBase := PICTTargetBWBase;
  207.             end; { else }
  208.  
  209.             { Post our exception handler. }
  210.  
  211.             CatchFailures(fi, HandleFailure);
  212.  
  213.             { Because the preferences file is open, we have to use CountResources }
  214.             { to get to the application's resource. }
  215.  
  216.             SetResLoad(FALSE);    { Do not need resource, just information. }
  217.  
  218.             for theCounter := 1 to CountResources('PICT') do begin
  219.                 rHandle := GetIndResource('PICT', theCounter);
  220.                 GetResInfo(rHandle, rID, rType, rName);
  221.                 if rID >= theTargetBase then begin
  222.                     itsNumTargetPICTs := itsNumTargetPICTs + 1;
  223.                 end; { if }
  224.  
  225.                 { Because the resource hasn't be loaded, we don't have to release it. }
  226.             end; { for }
  227.  
  228.             if not (itsNumTargetPICTS > 0) then begin
  229.                 if gSystem.hasColorQD then begin
  230.                     Failure(kSTRGameNoTargetPICTs, SpecifyMsg(STRlistGameMessages, kSTRGameNoTargetPICTs));
  231.                 end { if }
  232.                 else begin
  233.                     Failure(kSTRGameNoColorQDandNoBWTargetPICTs, SpecifyMsg(STRlistGameMessages, kSTRGameNoColorQDandNoBWTargetPICTs));
  234.                 end; { else }
  235.             end; { if }
  236.  
  237.             itsTargetPICTIDs := IDArrayH(NewHandleCanFail(SizeOf(Integer) * itsNumTargetPICTs));
  238.             FailNIL(itsTargetPICTIDs);
  239.  
  240.             theLoadCount := 1;
  241.             for theCounter := 1 to CountResources('PICT') do begin
  242.                 rHandle := GetIndResource('PICT', theCounter);
  243.                 GetResInfo(rHandle, rID, rType, rName);
  244.                 if rID >= theTargetBase then begin
  245. {$PUSH}
  246. {$R-}
  247.                     itsTargetPICTIDs^^[theLoadCount] := rID;
  248. {$POP}
  249.                     theLoadCount := theLoadCount + 1;
  250.                 end; { if }
  251.  
  252.                 { Because the resource hasn't be loaded, we don't have to release it. }
  253.             end; { for }
  254.  
  255.             SetResLoad(TRUE);
  256.  
  257.             { Panic's over. }
  258.  
  259.             Success;
  260.         end; { GetPICTIDsForTargets }
  261.  
  262.         procedure BuildWindowAndMainPane;
  263.  
  264.             var
  265.                 theWindow: CWindow;
  266.                 theWindRect: Rect;
  267.  
  268.                 theMenuBarHeight: Integer;
  269.                 theGDHandle: GDHandle;
  270.  
  271.                 thePicture: CPicture;
  272.  
  273.         begin { BuildWindowAndMainPane }
  274.             new(theWindow);
  275.             theWindow.IWindow(WINDGame, FALSE, gDesktop, SELF);
  276.             itsWindow := theWindow;
  277.  
  278.             { The window is initially hidden, set by the resource. }
  279.  
  280.             gDecorator.CenterWindow(theWindow);
  281.  
  282.             { Background picture. Start with the title screen. }
  283.  
  284.             new(thePicture);
  285.             itsMainPicture := thePicture;
  286.             itsMainPicture.IPicture(itsWindow, SELF, 0, 0, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY);
  287.             itsMainPicture.FitToEnclFrame(kDoHorizontal, kDoVertical);
  288.         end; { BuildWindowAndMainPane }
  289.  
  290.         procedure BuildTitleScreenPanes;
  291.  
  292.             var
  293.                 theButton: CButton;
  294.                 theScrollPane: CScrollPane;
  295.                 theLevelsTable: CShLevelsTable;
  296.                 theBorder: CPaneBorder;
  297.  
  298.         begin { BuildTitleScreenPanes }
  299.             { Start button. We have to create this before the levels table, because when the table is installed in the }
  300.             { levels scroll pane, this changes the selection, which calls the game director, and this sends a message }
  301.             { to the (assumed to exist) start button. }
  302.  
  303.             new(theButton);
  304.             itsStartButton := theButton;
  305.             itsStartButton.INewButton(kStartLenh, kStartLenv, kStartPosh, kStartPosv, 'Start', TRUE, 0, itsWindow, SELF);
  306.             itsStartButton.SetClickCmd(cmdStartGame);
  307.  
  308.             { Scroll pane for the table of levels. }
  309.  
  310.             new(theScrollPane);
  311.             itsLevelsPane := theScrollPane;
  312.             itsLevelsPane.IScrollPane(itsWindow, SELF, kTableLenh, kTableLenv, kTablePosh, kTablePosv, sizELASTIC, sizELASTIC, FALSE, TRUE, FALSE);
  313.  
  314.             { Array pane with the levels. }
  315.  
  316.             new(theLevelsTable);
  317.             itsLevelsTable := theLevelsTable;
  318.             itsLevelsTable.IShLevelsTable(itsLevelsPane, SELF, 0, 0, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY);
  319.             itsLevelsPane.InstallPanorama(itsLevelsTable);
  320.  
  321.             { The levels table intercepts keystrokes first, for scrolling through the levels. }
  322.  
  323.             itsGopher := itsLevelsTable;
  324.  
  325.             { Border for the table. }
  326.  
  327.             new(theBorder);
  328.             theBorder.IPaneBorder(kBorderLeft + kBorderTop + kBorderBottom);
  329.             itsLevelsTable.SetBorder(theBorder);
  330.         end; { BuildTitleScreenPanes }
  331.  
  332.         procedure BuildTiles;
  333.  
  334.             var
  335.                 theTileCount: TileRange;
  336.                 theTile: CShTile;
  337.  
  338.         begin { BuildTiles }
  339.             for theTileCount := 1 to kMaxTiles do begin
  340.                 new(theTile);
  341.                 itsTiles[theTileCount] := theTile;
  342.                 itsTiles[theTileCount].IShTile(itsWindow, SELF, (theTileCount - 1) mod kMaxGridAcross * kTileSize + kTargetPosh, (theTileCount - 1) div kMaxGridAcross * kTileSize + kTargetPosv, sizFIXEDSTICKY, sizFIXEDSTICKY);
  343.                 itsTiles[theTileCount].SetGridPos(theTileCount);
  344.             end; { for }
  345.         end; { BuildTiles }
  346.  
  347.         procedure BuildGameControls;
  348.  
  349.             var
  350.                 thePane: CPane;
  351.                 theIcon: CIconPane;
  352.  
  353.         begin { BuildGameControls }
  354.             { Controls for help, pausing/ resuming or aborting the game. }
  355.  
  356.             { Help icon is always visible. }
  357.  
  358.             new(theIcon);
  359.             theIcon.IIconPane(itsWindow, SELF, kHelpPosh, kHelpPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnHelp, TRUE);
  360.             theIcon.SetWantsClicks(TRUE);
  361.             theIcon.SetClickCmd(cmdHelp);
  362.  
  363.             { Dummy pane to enclose - easier to get the coordinates right and to hide the controls. }
  364.  
  365.             new(thePane);
  366.             itsPlayControls := thePane;
  367.             itsPlayControls.IPane(itsWindow, SELF, kControlsLenh, kControlsLenv, kControlsPosh, kControlsPosv, sizFIXEDSTICKY, sizFIXEDSTICKY);
  368.             itsPlayControls.SetWantsClicks(TRUE);
  369.  
  370.             new(theIcon);
  371.             itsPauseIcon := theIcon;
  372.             itsPauseIcon.IIconPane(itsPlayControls, SELF, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnPause, TRUE);
  373.             itsPauseIcon.SetWantsClicks(TRUE);
  374.             itsPauseIcon.SetClickCmd(cmdPauseGame);
  375.  
  376.             new(theIcon);
  377.             itsResumeIcon := theIcon;
  378.             itsResumeIcon.IIconPane(itsPlayControls, SELF, 0, 0, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnResume, TRUE);
  379.             itsResumeIcon.SetWantsClicks(TRUE);
  380.             itsResumeIcon.SetClickCmd(cmdResumeGame);
  381.  
  382.             new(theIcon);
  383.             theIcon.IIconPane(itsPlayControls, SELF, kIconPixels + kControlIconMargin, 0, sizFIXEDSTICKY, sizFIXEDSTICKY, cicnAbort, TRUE);
  384.             theIcon.SetWantsClicks(TRUE);
  385.             theIcon.SetClickCmd(cmdAbortGame);
  386.         end; { BuildGameControls }
  387.  
  388.         procedure BuildGameDataPanes;
  389.  
  390.             var
  391.                 thePane: CPane;
  392.                 theText: CEditText;
  393.  
  394.         begin { BuildGameDataPanes }
  395.  
  396.             { Text boxes for the name, moves and time. To reduce flicker, have hours, minutes and seconds. }
  397.  
  398.             new(thePane);
  399.             itsPlayData := thePane;
  400.             itsPlayData.IPane(itsWindow, SELF, kDataLenh, kDataLenv, kDataPosh, kDataPosv, sizFIXEDSTICKY, sizFIXEDSTICKY);
  401.  
  402.             new(theText);
  403.             itsNameText := theText;
  404.             itsNameText.IEditText(itsPlayData, SELF, kNameLenh, kNameLenv, kNamePosh, kNamePosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kNameLenh);
  405.             itsNameText.Specify(kNotEditable, kNotSelectable, kNotStylable);
  406.             itsNameText.SetAlignCmd(cmdAlignLeft);
  407.  
  408.             new(theText);
  409.             itsMovesText := theText;
  410.             itsMovesText.IEditText(itsPlayData, SELF, kMovesLenh, kMovesLenv, kMovesPosh, kMovesPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kMovesLenh);
  411.             itsMovesText.Specify(kNotEditable, kNotSelectable, kNotStylable);
  412.             itsMovesText.SetAlignCmd(cmdAlignLeft);
  413.  
  414.             new(theText);
  415.             itsHoursText := theText;
  416.             itsHoursText.IEditText(itsPlayData, SELF, kHoursLenh, kHoursLenv, kHoursPosh, kHoursPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kHoursLenh);
  417.             itsHoursText.Specify(kNotEditable, kNotSelectable, kNotStylable);
  418.             itsHoursText.SetAlignCmd(cmdAlignRight);
  419.  
  420.             new(theText);
  421.             itsMinutesText := theText;
  422.             itsMinutesText.IEditText(itsPlayData, SELF, kMinutesLenh, kMinutesLenv, kMinutesPosh, kMinutesPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kMinutesLenh);
  423.             itsMinutesText.Specify(kNotEditable, kNotSelectable, kNotStylable);
  424.             itsMinutesText.SetAlignCmd(cmdAlignCenter);
  425.  
  426.             new(theText);
  427.             itsSecondsText := theText;
  428.             itsSecondsText.IEditText(itsPlayData, SELF, kSecondsLenh, kSecondsLenv, kSecondsPosh, kSecondsPosv, sizFIXEDSTICKY, sizFIXEDSTICKY, kSecondsLenh);
  429.             itsSecondsText.Specify(kNotEditable, kNotSelectable, kNotStylable);
  430.             itsSecondsText.SetAlignCmd(cmdAlignCenter);
  431.         end; { BuildGameDataPanes }
  432.  
  433.     begin { IShGameDirector }
  434.         itsShApp := aSupervisor;
  435.  
  436.         itsMainPicture := nil;
  437.  
  438.         itsLevelsPane := nil;
  439.         itsLevelsTable := nil;
  440.         itsStartButton := nil;
  441.  
  442.         itsNameText := nil;
  443.         itsMovesText := nil;
  444.         itsHoursText := nil;
  445.         itsMinutesText := nil;
  446.         itsSecondsText := nil;
  447.  
  448.         itsHoursStr := '';
  449.         itsMinutesStr := '';
  450.         itsSecondsStr := '';
  451.  
  452.         itsPlayControls := nil;
  453.         itsPauseIcon := nil;
  454.         itsResumeIcon := nil;
  455.         for theTileCount := 1 to kMaxTiles do begin
  456.             itsTiles[theTileCount] := nil;
  457.         end; { for }
  458.  
  459.         itsTargetPICTIDs := nil;
  460.  
  461.         IDirector(aSupervisor);
  462.  
  463.         fPlaying := kNotPlaying;
  464.         fPaused := kNotPaused;
  465.  
  466.         itsStartLevel := kNoLevel;
  467.  
  468.         { At this point, the application and prefs file are well and truly established. }
  469.  
  470.         InitAnimationAndSound;
  471.  
  472.         { Because we are going to inspect the pictures ourselves, the preload bit }
  473.         { on all targets should be FALSE. }
  474.  
  475.         GetPICTIDsForTargets;
  476.  
  477.         { Array indices are 1.. itsNumTargetPICTs. For something different, use zero.}
  478.         itsLastPICTindex := 0;
  479.  
  480.         { Now build the window. }
  481.  
  482.         BuildWindowAndMainPane;
  483.         BuildTitleScreenPanes;
  484.         BuildTiles;
  485.         BuildGameControls;
  486.         BuildGameDataPanes;
  487.     end; { IShGameDirector }
  488.  
  489.  
  490. {****************************************************}
  491. {}
  492. {        Free                                                                                                                                                                                                                                }
  493. {}
  494. {     Destruction of the game director object. We set our pointers to nil, for the             }
  495. {        objects to which they were pointing will be disposed of in inherited methods.        }
  496. {}
  497. {****************************************************}
  498.  
  499.     procedure CShGameDirector.Free;
  500.  
  501.         var
  502.             theTileCount: TileRange;
  503.  
  504.     begin { Free }
  505.         itsShApp := nil;
  506.  
  507.         { The window disposes of all the subviews when it is freed. }
  508.         { For completeness, we set our pointers to nil. }
  509.  
  510.         itsMainPicture := nil;
  511.  
  512.         itsLevelsPane := nil;
  513.         itsLevelsTable := nil;
  514.         itsStartButton := nil;
  515.  
  516.         itsNameText := nil;
  517.         itsMovesText := nil;
  518.         itsHoursText := nil;
  519.         itsMinutesText := nil;
  520.         itsSecondsText := nil;
  521.  
  522.         itsPlayControls := nil;
  523.         itsPauseIcon := nil;
  524.         itsResumeIcon := nil;
  525.         for theTileCount := 1 to kMaxTiles do begin
  526.             itsTiles[theTileCount] := nil;
  527.         end; { for }
  528.  
  529.         ForgetHandle(itsTargetPICTIDs);
  530.  
  531.         inherited Free;
  532.     end; { Free }
  533.  
  534.  
  535. {****************************************************}
  536. {}
  537. {        DoCommand                                                                                                                                                                                                            }
  538. {}
  539. {        The game director is responsible for reacting to game controls.                                                }
  540. {        It also checks for the Help item being selected from the Help Menu.                                     }
  541. {}
  542. {****************************************************}
  543.  
  544.     procedure CShGameDirector.DoCommand (theCommand: longint);
  545.  
  546.         procedure DoConfigurePlayControls;
  547.  
  548.         begin { DoConfigurePlayControls }
  549.             if fPlaying then begin
  550.                 itsPlayControls.Show;
  551.                 if fPaused then begin
  552.                     itsResumeIcon.Show;
  553.                     itsPauseIcon.Hide;
  554.                 end { if }
  555.                 else begin
  556.                     itsPauseIcon.Show;
  557.                     itsResumeIcon.Hide;
  558.                 end; { else }
  559.             end { if }
  560.             else begin
  561.                 itsPlayControls.Hide;
  562.             end; { else }
  563.         end; { DoConfigurePlayControls }
  564.  
  565.         procedure DoSpecifyRandomTarget (aRedraw: Boolean);
  566.  
  567.             var
  568.                 theIndex: Integer;
  569.                 theTargetID: Integer;
  570.  
  571.                 theTileCount: TileRange;
  572.  
  573.                 theLRect: LongRect;
  574.                 theQDRect: Rect;
  575.  
  576.         begin { DoSpecifyRandomTarget }
  577.             SetCursor(gWatchCursor^^);
  578.  
  579.             theIndex := Abs(Random) mod itsNumTargetPICTs + 1;
  580.             if theIndex = itsLastPICTindex then begin
  581.                 { This is pretty unlikely (and unlucky). Cycle to the next one along. }
  582.                 theIndex := (theIndex + 1) mod itsNumTargetPICTs + 1;
  583.             end; { if }
  584.  
  585. {$PUSH}
  586. {$R-}
  587.             theTargetID := itsTargetPICTIDs^^[theIndex];
  588. {$POP}
  589.             itsLastPICTindex := theIndex;
  590.  
  591.             { Do the blitting. This is the slow part. }
  592.             for theTileCount := 1 to kMaxTiles do begin
  593.                 itsTiles[theTileCount].SetTargetPICT(theTargetID);
  594.             end; { for }
  595.  
  596.             { Now redraw. }
  597.             if aRedraw then begin
  598.                 { We can call DrawAll directly, rather than using Refresh and the usual }
  599.                 { window Update method, because we don't have to worry about the }
  600.                 { tile having subviews. This is faster and reduces flicker. }
  601.  
  602.                 for theTileCount := 1 to kMaxTiles do begin
  603.                     { Only redraw those tiles which are actually showing the picture. }
  604.  
  605.                     if itsTiles[theTileCount].GetState = Showing then begin
  606.                         itsTiles[theTileCount].GetFrame(theLRect);
  607.                         itsTiles[theTileCount].FrameToWindR(theLRect, theQDRect);
  608.                         itsTiles[theTileCount].DrawAll(theQDRect);
  609.                     end; { if }
  610.                 end; { for }
  611.             end; { if }
  612.         end; { DoSpecifyRandomTarget }
  613.  
  614.         procedure DoSetTilesToStart (aRedraw: Boolean);
  615.  
  616.             var
  617.                 theStartConfig: ConfigType;
  618.  
  619.                 theTileCount: TileRange;
  620.  
  621.                 theLRect: LongRect;
  622.                 theQDRect: Rect;
  623.  
  624.         begin { DoSetTilesToStart }
  625.             theStartConfig := CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel)).GetStart;
  626.             for theTileCount := 1 to kMaxTiles do begin
  627.                 if theStartConfig[theTileCount] then begin
  628.                     itsTiles[theTileCount].SetState(Hiding);
  629.                 end { if }
  630.                 else begin
  631.                     itsTiles[theTileCount].SetState(Showing);
  632.                 end; { else }
  633.             end; { for }
  634.  
  635.             { Now redraw. }
  636.             if aRedraw then begin
  637.                 { We can call DrawAll directly, rather than using Refresh and the usual }
  638.                 { window Update method, because we don't have to worry about the }
  639.                 { tile having subviews. This is faster and reduces flicker. }
  640.  
  641.                 for theTileCount := 1 to kMaxTiles do begin
  642.                     itsTiles[theTileCount].GetFrame(theLRect);
  643.                     itsTiles[theTileCount].FrameToWindR(theLRect, theQDRect);
  644.                     itsTiles[theTileCount].DrawAll(theQDRect);
  645.                 end; { for }
  646.             end; { if }
  647.         end; { DoSetTilesToStart }
  648.  
  649.         procedure DoStartGame;
  650.  
  651.             var
  652.                 theTileCount: TileRange;
  653.  
  654.         begin { DoStartGame }
  655.             fPlaying := kPlaying;
  656.             fPaused := kNotPaused;
  657.  
  658.             { Play the start game sound. }
  659.             SATSoundPlay(gSoundGameStarting, 9, TRUE);
  660.             SATSoundEvents;
  661.  
  662.             itsLevelsPane.Hide;
  663.             itsStartButton.Hide;
  664.  
  665.             if gSystem.hasColorQD then begin
  666.                 itsMainPicture.UsePICT(PICTGameColour);
  667.             end { if }
  668.             else begin
  669.                 itsMainPicture.UsePICT(PICTGameBW);
  670.             end; { else }
  671.             itsMainPicture.Refresh;
  672.  
  673.             { Specify, but don't draw yet. This will be done }
  674.             { in the window's Update method. }
  675.             DoSpecifyRandomTarget(FALSE);
  676.  
  677.             { Set the tiles, but don't draw yet. This will be done }
  678.             { in the window's Update method. }
  679.             DoSetTilesToStart(FALSE);
  680.             for theTileCount := 1 to kMaxTiles do begin
  681.                 { Showing the tiles refreshes them also. }
  682.                 itsTiles[theTileCount].Show;
  683.             end; { for }
  684.  
  685.             DoConfigurePlayControls;
  686.             itsPlayData.Show;
  687.  
  688.             itsNameText.Show;
  689.             itsNameText.SetTextString(CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel)).GetName);
  690.  
  691.             { Initialize the clock. }
  692.  
  693.             itsLastStartTime := TickCount;
  694.             itsTimeBeforeLastStart := 0;
  695.             itsTimeSinceLastStart := 0;
  696.             itsTimeSinceLastStartOld := 0;
  697.  
  698.             itsHoursStr := '0';
  699.             itsHoursText.SetTextString(itsHoursStr);
  700.             itsHoursText.Show;
  701.  
  702.             itsMinutesStr := '00';
  703.             itsMinutesText.SetTextString(itsMinutesStr);
  704.             itsMinutesText.Show;
  705.  
  706.             itsSecondsStr := '00';
  707.             itsSecondsText.SetTextString(itsSecondsStr);
  708.             itsSecondsText.Show;
  709.  
  710.             { Initialize the number of moves made. }
  711.  
  712.             itsMovesText.Show;
  713.             itsMoves := 0;
  714.             itsMovesText.SetTextString('0');
  715.  
  716.             itsWindow.Update;
  717.         end; { DoStartGame }
  718.  
  719.         procedure DoPauseGame;
  720.  
  721.             var
  722.                 theTileCount: TileRange;
  723.  
  724.         begin { DoPauseGame }
  725.             itsTimeBeforeLastStart := (itsTimeBeforeLastStart - itsLastStartTime + TickCount) mod kLoopTime;
  726.  
  727.             fPaused := kPaused;
  728.  
  729.             for theTileCount := 1 to kMaxTiles do begin
  730.                 itsTiles[theTileCount].SetWantsClicks(FALSE);
  731.             end; { for }
  732.  
  733.             DoConfigurePlayControls;
  734.  
  735.             itsWindow.Update;
  736.         end; { DoPauseGame }
  737.  
  738.         procedure DoResumeGame;
  739.  
  740.             var
  741.                 theTileCount: TileRange;
  742.  
  743.         begin { DoResumeGame }
  744.             itsLastStartTime := TickCount;
  745.             itsTimeSinceLastStart := 0;
  746.             itsTimeSinceLastStartOld := 0;
  747.  
  748.             fPaused := kNotPaused;
  749.  
  750.             for theTileCount := 1 to kMaxTiles do begin
  751.                 itsTiles[theTileCount].SetWantsClicks(TRUE);
  752.             end; { for }
  753.  
  754.             DoConfigurePlayControls;
  755.  
  756.             itsWindow.Update;
  757.         end; { DoResumeGame }
  758.  
  759.         procedure DoAbortGame;
  760.  
  761.         begin { DoAbortGame }
  762.             fPlaying := kNotPlaying;
  763.             fPaused := kNotPaused;
  764.  
  765.             { Play the game abort sound. }
  766.             SATSoundPlay(gSoundGameAborted, 9, TRUE);
  767.             SATSoundEvents;
  768.  
  769.             DisplayTitleScreen(FALSE);
  770.         end; { DoAbortGame }
  771.  
  772.         procedure DoAboutAndHelp (aCommand: LongInt);
  773.  
  774.         begin { DoAboutAndHelp }
  775.             if fPaused then begin
  776.                 inherited DoCommand(aCommand); { Pass onto application. }
  777.             end { if }
  778.             else begin
  779.                 DoPauseGame;
  780.                 inherited DoCommand(aCommand); { Pass onto application. }
  781.                 DoResumeGame;
  782.             end; { else }
  783.         end; { DoAboutAndHelp }
  784.  
  785.         procedure DoSetAnimationOrSound;
  786.  
  787.             var
  788.                 theFlagHandle: BooleanHandle;
  789.  
  790.         begin { DoSetAnimationOrSound }
  791.             theFlagHandle := nil;
  792.  
  793.             SetCriticalOperation(TRUE);
  794.             theFlagHandle := BooleanHandle(NewHandleCanFail(SizeOf(Boolean)));
  795.             FailNIL(theFlagHandle);
  796.             SetCriticalOperation(FALSE);
  797.  
  798.             if theCommand = cmdAnimationOn then begin
  799.                 fAnimate := not fAnimate;
  800.                 theFlagHandle^^ := fAnimate;
  801.                 itsShApp.PreferencesFile.SetPref(kPrefAnimationOn, Handle(theFlagHandle));
  802.             end { if }
  803.             else begin
  804.                 fSound := not fSound;
  805.                 theFlagHandle^^ := fSound;
  806.                 itsShApp.PreferencesFile.SetPref(kPrefSoundOn, Handle(theFlagHandle));
  807.  
  808.                 if fSound then begin
  809.                     SATSoundOn;
  810.                 end { if }
  811.                 else begin
  812.                     SATSoundOff;
  813.                 end; { else }
  814.             end; { else }
  815.  
  816.             ForgetHandle(theFlagHandle);
  817.         end; { DoSetAnimationOrSound }
  818.  
  819.     begin { DoCommand }
  820.         if HiWord(-theCommand) = kHMHelpMenuID then begin
  821.             DoAboutAndHelp(cmdHelp);
  822.         end { if }
  823.         else begin
  824.             case theCommand of
  825.  
  826.                 cmdAbout, cmdHelp:  begin
  827.                     DoAboutAndHelp(theCommand);
  828.                 end; { cmdAbout, cmdHelp }
  829.  
  830.                 cmdStartGame:  begin
  831.                     DoStartGame;
  832.                 end; { cmdStartGame }
  833.  
  834.                 cmdPauseGame:  begin
  835.                     DoPauseGame;
  836.                 end; { cmdPauseGame }
  837.  
  838.                 cmdResumeGame:  begin
  839.                     DoResumeGame;
  840.                 end; { cmdResumeGame }
  841.  
  842.                 cmdAbortGame:  begin
  843.                     DoAbortGame;
  844.                 end; { cmdAbortGame }
  845.  
  846.                 cmdClickStart:  begin
  847.                     itsStartButton.SimulateClick;
  848.                 end; { cmdClickStart }
  849.  
  850.                 cmdAnimationOn, cmdSoundOn:  begin
  851.                     DoSetAnimationOrSound;
  852.                 end; { cmdAnimationOn, cmdSoundOn }
  853.  
  854.                 cmdChangeTargetPicture:  begin
  855.                     DoSpecifyRandomTarget(TRUE);
  856.                 end; { cmdChangeTargetPicture }
  857.  
  858.                 cmdResetTiles:  begin
  859.                     DoSetTilesToStart(TRUE);
  860.                 end; { cmdResetTiles }
  861.  
  862.                 otherwise begin
  863.                     inherited DoCommand(theCommand); { Invoke inherited method to handle other commands }
  864.                 end; { otherwise }
  865.             end; { case}
  866.         end; { else }
  867.     end; { DoCommand }
  868.  
  869.  
  870. {****************************************************}
  871. {}
  872. {        DoKeyDown                                                                                                                                                                                                            }
  873. {}
  874. {     Check for the help key being pressed.                                                                                                                             }
  875. {}
  876. {****************************************************}
  877.  
  878.     procedure CShGameDirector.DoKeyDown (theChar: char;
  879.                                     keyCode: Byte;
  880.                                     macEvent: EventRecord);
  881.  
  882.     begin { DoKeyDown }
  883.         if keyCode = KeyHelp then begin
  884.             DoCommand(cmdHelp);
  885.         end; { if }
  886.         inherited DoKeyDown(theChar, keyCode, macEvent);
  887.     end; { DoKeyDown }
  888.  
  889.  
  890. {****************************************************}
  891. {}
  892. {        DoTileClicked                                                                                                                                                                                                    }
  893. {}
  894. {     Respond to a click on the given tile.                                                                                                                                    }
  895. {}
  896. {****************************************************}
  897.  
  898.     procedure CShGameDirector.DoTileClicked (aTileNum: TileRange);
  899.  
  900.         procedure FlipTiles;
  901.  
  902.             var
  903.                 theRule: TileRuleType;
  904.  
  905.                 theTileCount: TileRange;
  906.  
  907.                 ignoreTime: LongInt;
  908.  
  909.         begin { FlipTiles }
  910.             theRule := CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel)).GetRules[aTileNum];
  911.             with theRule do begin
  912.                 for theTileCount := 1 to theNumDep do begin
  913.                     itsTiles[theDep[theTileCount]].DoFlip(fAnimate);
  914.                     Dawdle(ignoreTime); { Give some time to update the clock. }
  915.                 end; { for }
  916.             end; { with }
  917.         end; { FlipTiles }
  918.  
  919.         function TargetIsShowing: Boolean;
  920.  
  921.             var
  922.                 theResult: Boolean;
  923.  
  924.                 theTileCount: TileRange;
  925.  
  926.         begin { TargetIsShowing }
  927.             theResult := TRUE;
  928.  
  929.             { Strictly, we shouldn't use a for loop, because it can terminate early. }
  930.             { However, the difference isn't all that much - and I don't want to use Leave. }
  931.  
  932.             for theTileCount := 1 to kMaxTiles do begin
  933.                 theResult := theResult & (itsTiles[theTileCount].GetState = Showing);
  934.             end; { for }
  935.  
  936.             TargetIsShowing := theResult;
  937.         end; { TargetIsShowing }
  938.  
  939.         procedure UpdateMovesCounter;
  940.  
  941.             var
  942.                 theCountStr: Str255;
  943.  
  944.         begin { UpdateMovesCounter }
  945.             itsMoves := itsMoves + 1;
  946.             NumToString(itsMoves, theCountStr);
  947.             itsMovesText.SetTextString(theCountStr);
  948.  
  949.             itsWindow.Update;
  950.         end; { UpdateMovesCounter }
  951.  
  952.         procedure GameCompleted;
  953.  
  954.             const
  955.                 kEndGameDelay = 60;
  956.  
  957.             var
  958.                 theDelayStart: LongInt;
  959.  
  960.                 theLevel: CShLevel;
  961.  
  962.                 theTileCount: TileRange;
  963.                 theBestDirector: CShBestPlayDirector;
  964.                 theName: Str15;
  965.  
  966.         begin { GameCompleted }
  967.             { Stop the clock. }
  968.  
  969.             itsTimeSinceLastStart := (TickCount - itsLastStartTime) mod kLoopTime;
  970.  
  971.             fPlaying := kNotPlaying;
  972.             fPaused := kNotPaused;
  973.  
  974.             { Play the game complete sound. }
  975.             SATSoundPlay(gSoundGameComplete, 9, TRUE);
  976.             SATSoundEvents;
  977.  
  978.             { Update the clock. }
  979.             itsWindow.Update;
  980.  
  981.             { Let the user have some time to admire their handiwork. }
  982.             theDelayStart := TickCount;
  983.             while TickCount < theDelayStart + kEndGameDelay do begin
  984.             end; { while }
  985.  
  986.             theLevel := CShLevel(CList(itsLevelsTable.GetArray).NthItem(itsStartLevel));
  987.  
  988.             { Best criteria is the number of moves. This way, equal awards }
  989.             { for having animation on or off. }
  990.             if itsMoves < theLevel.GetMoves then begin
  991.  
  992.                 { Get the player's name. }
  993.  
  994.                 new(theBestDirector);
  995.                 theBestDirector.IShBestPlayDirector(SELF);
  996.                 theBestDirector.GetPlayerName(theName);
  997.                 ForgetObject(theBestDirector);
  998.  
  999.                 { First update the level concerned. }
  1000.  
  1001.                 theLevel.SetPlayer(theName);
  1002.                 theLevel.SetMoves(itsMoves);
  1003.                 theLevel.SetTime(itsTimeBeforeLastStart + itsTimeSinceLastStart);    { < maxLongInt by construction. }
  1004.  
  1005.                 { We send the best player information to the application, which }
  1006.                 { decides whether to store it in the preference file (default levels) }
  1007.                 { or in the active document. }
  1008.  
  1009.                 itsShApp.StoreBestPlayer(itsStartLevel, theName, itsMoves, itsTimeBeforeLastStart + itsTimeSinceLastStart);
  1010.  
  1011.             end; { if }
  1012.         end; { GameCompleted }
  1013.  
  1014.     begin { DoTileClicked }
  1015.         FlipTiles;
  1016.         UpdateMovesCounter;
  1017.         if TargetIsShowing then begin
  1018.             GameCompleted;
  1019.             DisplayTitleScreen(TRUE);
  1020.         end; { if }
  1021.     end; { DoTileClicked }
  1022.  
  1023.  
  1024. {****************************************************}
  1025. {}
  1026. {        DisplayTitleScreen                                                                                                                                                                                    }
  1027. {}
  1028. {     Display the title screen. Possible cases are on startup, after a level is                            }
  1029. {        completed, or a game is aborted.                                                                                                                                            }
  1030. {}
  1031. {****************************************************}
  1032.  
  1033.     procedure CShGameDirector.DisplayTitleScreen (aCompletedLevel: Boolean);
  1034.  
  1035.         var
  1036.             theTileCount: TileRange;
  1037.             theStartCell: Cell;
  1038.  
  1039.     begin { DisplayTitleScreen }
  1040.  
  1041.         if gSoundWelcome <> nil then begin
  1042.             { We play the welcoming sound once and only once. }
  1043.             { It will play asynchronously. }
  1044.             if not fWelcomingSoundPlayed then begin
  1045.                 SATSoundPlay(gSoundWelcome, 9, FALSE);
  1046.                 SATSoundEvents;
  1047.  
  1048.                 fWelcomingSoundPlayed := TRUE;
  1049.             end { if }
  1050.             else begin
  1051.                 { Having played this sound, we won't need it again. }
  1052.                 { We dispose of it now, as it releases a sizable chunk of memory. }
  1053.  
  1054.                 { Have to play it safe, and wait for all possible sounds to end }
  1055.                 { before disposing of it. It is possible that our welcoming sound }
  1056.                 { may still be playing. }
  1057.  
  1058.                 while not SATSoundDone do begin
  1059.                     SATSoundEvents;
  1060.                 end; { while }
  1061.  
  1062.                 SATDisposeSound(gSoundWelcome);
  1063.                 gSoundWelcome := nil;
  1064.             end; { else }
  1065.         end; { if }
  1066.  
  1067.         itsPlayControls.Hide;
  1068.         itsPlayData.Hide;
  1069.  
  1070.         for theTileCount := 1 to kMaxTiles do begin
  1071.             itsTiles[theTileCount].Hide;
  1072.         end; { for }
  1073.  
  1074.         itsLevelsPane.Show;
  1075.         itsStartButton.Show;
  1076.  
  1077.         if gSystem.hasColorQD then begin
  1078.             itsMainPicture.UsePICT(PICTTitleColour);
  1079.         end { if }
  1080.         else begin
  1081.             itsMainPicture.UsePICT(PICTTitleBW);
  1082.         end; { else }
  1083.         itsMainPicture.Refresh;
  1084.  
  1085.         { If no level has been previously played, start at the first one, otherwise }
  1086.         { select the level after the one which has been successfully played. }
  1087.         { We do this last, so that the start button is updated. }
  1088.         { Recall that cell indices are one less than "real" indices. }
  1089.  
  1090.         if itsStartLevel = kNoLevel then begin
  1091.             SetCell(theStartCell, 0, kNoLevel);
  1092.         end { if }
  1093.         else if aCompletedLevel then begin
  1094.             SetCell(theStartCell, 0, itsStartLevel);
  1095.         end { else if }
  1096.         else begin
  1097.             SetCell(theStartCell, 0, itsStartLevel - 1);
  1098.         end; { else }
  1099.  
  1100.         itsLevelsTable.SelectCell(theStartCell, FALSE, FALSE);
  1101.         itsLevelsTable.ScrollToSelection;
  1102.  
  1103.     end; { DisplayTitleScreen }
  1104.  
  1105.  
  1106. {****************************************************}
  1107. {}
  1108. {        UpdateMenus                                                                                                                                                                                                        }
  1109. {}
  1110. {     The game director is responsible for all commands related to playing the                     }
  1111. {     game, and the user preferences for animation and sound.                                                                     }
  1112. {}
  1113. {****************************************************}
  1114.  
  1115.     procedure CShGameDirector.UpdateMenus;
  1116.  
  1117.     begin { UpdateMenus }
  1118.         inherited UpdateMenus;
  1119.  
  1120.         if fPlaying then begin
  1121.             gBartender.EnableCmd(cmdAbortGame);
  1122.  
  1123.             if fPaused then begin
  1124.                 gBartender.EnableCmd(cmdResumeGame);
  1125.             end { if }
  1126.             else begin
  1127.                 gBartender.EnableCmd(cmdPauseGame);
  1128.             end; { else }
  1129.  
  1130.             gBartender.EnableCmd(cmdChangeTargetPicture);
  1131.             gBartender.EnableCmd(cmdResetTiles);
  1132.         end { if }
  1133.         else begin
  1134.             gBartender.EnableCmd(cmdStartGame);
  1135.         end; { else }
  1136.  
  1137.         gBartender.CheckMarkCmd(cmdAnimationOn, fAnimate);
  1138.         gBartender.EnableCmd(cmdAnimationOn);
  1139.         gBartender.CheckMarkCmd(cmdSoundOn, fSound);
  1140.         gBartender.EnableCmd(cmdSoundOn);
  1141.     end; { UpdateMenus }
  1142.  
  1143.  
  1144. {****************************************************}
  1145. {}
  1146. {        ProviderChanged                                                                                                                                                                                            }
  1147. {}
  1148. {     The levels table communicates with the game director through the provider         }
  1149. {     changed mechanism. If the list of levels has changed its selection, it records         }
  1150. {        the fact and informs the start button.                                                                                                                             }
  1151. {}
  1152. {****************************************************}
  1153.  
  1154.     procedure CShGameDirector.ProviderChanged (aProvider: CCollaborator;
  1155.                                     reason: Longint;
  1156.                                     info: univ Ptr);
  1157.  
  1158.         var
  1159.             theRow: LevelsRange;
  1160.             theStr255: Str255;
  1161.  
  1162.     begin { ProviderChanged }
  1163.         if reason = tableSelectionChanged then begin
  1164.  
  1165.             GetIndString(theStr255, STRlistGameMessages, kSTRGameStartIndex);
  1166.  
  1167.             theRow := itsLevelsTable.RowSelected;
  1168.             if theRow = kNoLevel then begin
  1169.                 itsStartButton.SetTitle(theStr255);
  1170.                 itsStartButton.Deactivate;
  1171.             end { if }
  1172.             else if theRow <> itsStartLevel then begin
  1173.                 itsStartButton.SetTitle(Concat(theStr255, ' “', CShLevel(CList(itsLevelsTable.GetArray).NthItem(theRow)).GetName, '”'));
  1174.                 itsStartButton.Activate;
  1175.             end; { else if }
  1176.  
  1177.             itsStartLevel := theRow;
  1178.  
  1179.         end; { if }
  1180.  
  1181.         inherited ProviderChanged(aProvider, reason, info);
  1182.     end; { ProviderChanged }
  1183.  
  1184.  
  1185. {****************************************************}
  1186. {}
  1187. {        Dawdle                                                                                                                                                                                                                        }
  1188. {}
  1189. {        If the game is progressing, update the clock.                                                                                                         }
  1190. {}
  1191. {****************************************************}
  1192.  
  1193.     procedure CShGameDirector.Dawdle (var maxSleep: longint);
  1194.  
  1195.         var
  1196.             hoursStr, minutesStr, secondsStr: Str2;
  1197.             theStr255: Str255;
  1198.  
  1199.         procedure GetTimeStrs (aDuration: LongInt;
  1200.                                         var aHoursStr, aMinutesStr, aSecondsStr: Str2);
  1201.  
  1202.             var
  1203.                 theStr255: Str255;
  1204.  
  1205.         begin { GetTimeStrs }
  1206.             aDuration := aDuration div 60;
  1207.             NumToString(aDuration mod 60, theStr255);
  1208.  
  1209.             aSecondsStr := Copy(theStr255, 1, Length(theStr255));
  1210.             if Length(aSecondsStr) < 2 then begin
  1211.                 aSecondsStr := Concat('0', aSecondsStr);
  1212.             end; { if }
  1213.  
  1214.             aDuration := aDuration div 60;
  1215.             NumToString(aDuration mod 60, theStr255);
  1216.             aMinutesStr := Copy(theStr255, 1, Length(theStr255));
  1217.             if Length(aMinutesStr) < 2 then begin
  1218.                 aMinutesStr := Concat('0', aMinutesStr);
  1219.             end; { if }
  1220.  
  1221.             aDuration := aDuration div 60;
  1222.             NumToString(aDuration, theStr255);
  1223.             aHoursStr := Copy(theStr255, 1, Length(theStr255));
  1224.         end; { GetTimeStrs }
  1225.  
  1226.     begin { Dawdle }
  1227.         if fPlaying & not fPaused then begin
  1228.             itsTimeSinceLastStart := TickCount - itsLastStartTime;
  1229.             if itsTimeSinceLastStart - itsTimeSinceLastStartOld > 60 then begin
  1230.  
  1231.                 GetTimeStrs(itsTimeBeforeLastStart + itsTimeSinceLastStart, hoursStr, minutesStr, secondsStr);
  1232.                 theStr255 := '';
  1233.  
  1234.                 if itsHoursStr <> hoursStr then begin
  1235.                     itsHoursStr := hoursStr;
  1236.                     theStr255 := Copy(itsHoursStr, 1, Length(itsHoursStr));
  1237.                     itsHoursText.SetTextString(theStr255);
  1238.                 end; { if }
  1239.  
  1240.                 if itsMinutesStr <> minutesStr then begin
  1241.                     itsMinutesStr := minutesStr;
  1242.                     theStr255 := Copy(itsMinutesStr, 1, Length(itsMinutesStr));
  1243.                     itsMinutesText.SetTextString(theStr255);
  1244.                 end; { if }
  1245.  
  1246.                 if itsSecondsStr <> secondsStr then begin
  1247.                     itsSecondsStr := secondsStr;
  1248.                     theStr255 := Copy(itsSecondsStr, 1, Length(itsSecondsStr));
  1249.                     itsSecondsText.SetTextString(theStr255);
  1250.                 end; { if }
  1251.  
  1252.                 itsTimeSinceLastStartOld := itsTimeSinceLastStart;
  1253.             end; { if }
  1254.             maxSleep := kActivePlaySleepTime;
  1255.         end; { if }
  1256.     end; { Dawdle }
  1257.  
  1258.  
  1259. {****************************************************}
  1260. {}
  1261. {        SetLevels                                                                                                                                                                                                                }
  1262. {}
  1263. {        Installs the specified list of levels in the table.                                                                                                 }
  1264. {}
  1265. {****************************************************}
  1266.  
  1267.     procedure CShGameDirector.SetLevels (aLevels: CList);
  1268.  
  1269.     begin { SetLevels }
  1270.         itsLevelsTable.SetArray(aLevels, TRUE);
  1271.  
  1272.         { If a new set of levels is in place, then it is effectively like starting out again. }
  1273.         { ie no level has been previously played. }
  1274.  
  1275.         itsStartLevel := kNoLevel;
  1276.     end; { SetLevels }
  1277.  
  1278.  
  1279. {****************************************************}
  1280. {}
  1281. {        IsPlaying                                                                                                                                                                                                                    }
  1282. {}
  1283. {        Returns TRUE if playing, FALSE otherwise.                                                                                                            }
  1284. {}
  1285. {****************************************************}
  1286.  
  1287.     function CShGameDirector.IsPlaying: Boolean;
  1288.  
  1289.     begin { IsPlaying }
  1290.         IsPlaying := fPlaying;
  1291.     end; { IsPlaying }
  1292.  
  1293. end. { CShGameDirector }